#copyright 2009 Kevin Daley
##This file is a part of Nyst.
#Nyst is free software: you can redistribute it and/or modify
 #   it under the terms of the GNU Lesser General Public License as published by
  #  the Free Software Foundation, either version 3 of the License, or
   # (at your option) any later version.

   # This program is distributed in the hope that it will be useful,
   # but WITHOUT ANY WARRANTY; without even the implied warranty of
   # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   # GNU Lesser General Public License for more details.

   # You should have received a copy of the GNU Lesser General Public License
   # along with this program.  If not, see <http://www.gnu.org/licenses/>.

#Kevin Daley, Lead Developer.  still.horse@gmail.com.

try:
	import sys
	import random
	import math
	import os
	import getopt
	import pygame
	import numpy
	import time
	from socket import *
	from pygame.locals import *
	import map, agent
except ImportError, err:
	print "couldn't load module. %s" % (err)
	sys.exit(2)

class loader(Singleton):
	def init(self, *args):
		self.dict={}
	def load_png(name):
		if(name not in self.dict.keys()):
			""" Load image and return image object"""
			fullname = os.path.join('data', name)
			try:
				image = pygame.image.load(fullname)
				if image.get_alpha() is None:
					image = image.convert()
				else:
					image = image.convert_alpha()
			except pygame.error, message:
				print 'Cannot load image:', fullname
				raise SystemExit, message
			self.dict[name]=image
			return image
		return self.dict[name]

class spritefield(pygame.sprite.Sprite):
	def __init__(self, x, y, image, n):
		pygame.sprite.Sprite.__init__(self)
		self.image, self.rect = image
		self.rect=self.rect.move([x,y])
		self.n=n
	def incr(self,n):
		self.n+=n
	def render(self):
		for i in xrange(n):
			pygame.sprite.RenderPlain(self)


	
class Environment(map.node):
	def __init__(self, ID, number, cell_width, network, attributes, cam=(50,50),(parents={}, children={}, offset, neighbors={}), (estring=None, cstrings=None, sstrings=None))
		screen=pygame.display.get_surface()
		map.node.__init__(ID, (parents={}, children={}, offset, neighbors={}), (estring, cstrings, sstrings),((screen.get_width(), screen.get_height()), attributes, network,1))
		self.imgs=[]
		self.spritefield=[]
		for k in xrange(len(self.spectrum)):
			if(self.attributes[k][0]!="_"):
				self.imgs.append(load_png(self.spectrum[k]))
				for i in xrange(self.res_x):
					for j in xrange(self.res_y):
						k=spritefield(i+self.offset[0]-cam,j+self.offset[1]-cam, imgs[k], self.number[k,i,j])
						self.spritefield.append(k)
		self.cam=cam
	def scroll(self, cam_new):
		for i in self.spritefield:
			i.rect.move(cam_new)
		i.render()	
	def zoom(self, l):
		k=numpy.array(self.parents.getvals)
		if(l!=1):			
			return numpy.array([i.zoom(l-1) for i in k]).flatten()
		else:
			return k		
	def update(self):
		for k in xrange(len(self.attributes)):
			for i in xrange(self.res_x):
				for j in xrange(self.res_y):
					self.spritefield.append(spritefield(i+self.offset[0]-cam,j+self.offset[1]-cam, imgs[k], self.number[k,i,j]))

    
class InstanceFactory(type, list):
     def __init__(self, *args):
	     list.__init__(*args)
	     for j in self:
			for i in self:
				 if int(j[len(j)-1])<=int(i[len(i)-1]):
					 self.append(j+i)
     def call(self, *args, n):
	if(n!=0):
		obj = type.__call__(self, *args)
		obj.cast_identity(name, spectrum=self)
		next=self.__call__(*args, n-1)
		obj.next=next
		return obj
	return None
	
class Entity(agent.d00d, pygame.sprite.Sprite):
	next=None
	__metaclass__=InstanceFactory
	def __init__(self, location, node, name, ID, state):
		self.name=name
        	pygame.sprite.Sprite.__init__(self)
		img=loader()
		self.image=img(name)
		self.rect = self.image.get_rect()
        	self.rect.left = location[0]
		self.rect.top=location[1]
		self.location=location
		if(state!=None):
			self.state=state
	def bank(dict_S)
		self.state_d=dict_S
	def __iter__(self):
		return self
	def next(self):
		return self.next
	def attach(self, env):
		self.env=env
		for i in self.node.spectrum:
				j=self.env.spectrum.index("_"+i)
				tmp=i
				self.node.spectrum[j]=i
				i=tmp
	def update(self, env, current_time, event_callback):
		self.env=env
		self.image=self.state_d[name+state][0]
		if self.next_update_time < current_time:
		   self.env[name]=self.node.number[self.state][self.location[0]][self.location[1]]+self.node.number[event_callback(current_time)][self.location[0]][self.location[1]]+(self.node.interpretpgm[self.goal])-self.goal_val
		   self.env.interpretpgm("0"+name+"=#.[%[\\"+ID.split("::").pop()+"]]"))
		   self.image=load_png(self.env["0"+name])
	           self.rect.top+=self.env.interpretpgm("#[.[%[\\"+ID.split("::").pop()+"]]]"))
		   self.location[0]=self.rect.left
		   self.location[1]=self.rect.top
		   self.next_update_time = current_time + int(-1*(self.env.interpretpgm(".[%[\\"+ID.split("::").pop()+"]]")-crowd)**2/(node.res_x*node.res_y))
		   return self
		
	def queue_update(self, mg_node, event_callback):
		self.env.queue.append(lambda t: self.update(mg_node, t, event_callback))
	def kill(self):
		self.state="dead0"
		self.env.queue.append(lambda t: self.__delete__())
class NodeManager(Environment, pygame.sprite.RenderUpdates):
	def __init__(self, *args):
		Environment.__init__(*args)
		RenderUpdates.__init__()
		self.queue=[]
	def update_entities(current_time):
		self.__call__()
		for i in self.queue:
				self.add(i(current_time))
		for i in self.children.keys():
				self.children[i].update_entities()
		
class Singleton(object):
    def __new__(cls, *args, **kwds):
        it = cls.__dict__.get("__it__")
        if it is not None:
            return it
        cls.__it__ = it = object.__new__(cls)
        it.init(*args, **kwds)
        return it
    def init(self, *args, **kwds):
        pass
class RootNode(Singleton, NodeManager):
	def init(*args, **kwds):
		NodeManager.__init__(*args)
	def __call__(screen):
		self.update_entities(time.time())
		pygame.display.update(self.draw(screen))
class IDs(Singleton, dict):
	pass
class game(Singleton, list):
	__metaclass__=InstanceFactory
	def init(*args):
		list.__init__(*args)
	Factory={}
	zones=[]
	def __imod__(spectrum):
		self.spectrum.extend(spectrum)
	def __iadd__(meta):
		self.meta=meta
		for meta.Factory.iterkeys():
				self.Factory[i]=meta.Factory[i]
	def load_entities(filename, Factory=None):
		if(Factory!=None):
			for i=Factory.iterkeys():
				self.Factory[i]=Factory[i]
		f=open(filename)
		if(".dat" in filename):
			while( i=f.readline() and i!=None):
				self.load_entities(i)
		else:
			f=open(filename).read()
			pos=re.compile("\((?P<pos_x>[0-9,\.]*),(?P<pos_y>[0-9,\.]*)\)$").search(f)
			n=int(re.compile("(?P<number>n=[0-9]*$)").search(f).group(0))
			k=re.compile("(?P<numberk>k=[0-9]*$)").search(f).group(0)
			ID=re.compile("(?P<ID>ID=[0-9,:]*$)").search(f).group(0)
			name=re.compile("(?P<ID>name=[0-9]*$)").search(f).group(0)
			node=re.compile("(?P<node>node=.*$)").search(f).group(0)
			node=re.compile("(?P<state>state=.*$)").search(f).group(0)
			for i in xrange(n):
				x=float(pos.group("pos_x"))[i]
				y=float(pos.group("pos_y"))[i]
				newname=name[i]
				newnumber=int(k[i])
				newID=ID[i]
				newstate=state[i]
				newlocation=(x,y) 
				self.append(self.Factory[ID](newlocation, self.load_agent_state_graph(node[i]), newname, newID, newstate, newnumber, self.meta.bank))
				self[i].attach(self.zones[i])
	def load_agent_state_graph(node):
		return numpy.fromfile(node)
	def main(self):
		self.root=RootNode(self.meta.level, numpy.fromfile(self.meta.level_filename[0]), 1, numpy.fromfile(self.meta.level_env_node[0]),  self.spectrum)
		for i in xrange(len(self.meta.zones)):
				k=engine.NodeManager(self.meta.zone[i], numpy.fromfile(self.meta.level_filename[i+1]), 1, numpy.fromfile(self.meta.level_env_node[i+1]),  self.spectrum)
				self.zones.append(k)
				self.zones[self.meta.alpha*i]+=k
		while(1):
			self.meta(self.root)
			
class meta(Singleton):
	Factory={}
	def __init__(self, alpha,beta):
		self.time=0
		self.alpha=alpha
		self.level=random.random()
		n=(1-alpha)**beta/(1-alpha)
		self.zone=[random.random() for i in xrange(n)]
		self.level_filename=["level"+str(zone[i]) for i in xrange(n)]
		self.level_env_node=["node"+str(zone[i]) for i in xrange(n)]
		self.bank={}
	def describe_state(self, sprite, images, indices, times):
			for i in images:
				bank[sprite]=(i, indices, times[i])
	def __iadd__(self, Entity):
		self.Factory[Entity.typeID]=Entity
	def __call__(self, callback):
		for i=self.bank.iterkeys():
			if(time.time()-self.time>=self.bank[i][2]):
				self.bank[i]=(self.bank[self.bank[i][1]][0], self.bank[i][1], self.bank[self.bank[i][1]][2])
		self.time=time.time()
		callback()

	
			



	
				